/**
 * Compute term weights based on movie similarity; this is the reverse of what
 * we usually do.
 *
 * Usage:
 * ... similarity_size similarity_bin coratings_bin [d] [beta] <terms
 *
 * similarity_size: number of rows in similarity matrix; positive integer.
 * similarity_bin: num_movies by num_movies symmetric_matrix of floats in
 *                 binary format = similarity score in [-1, 1] for each
 *                 movie-movie pair. These come from ratings data.
 * coratings_bin:  num_movies by num_movies symmetric_matrix of floats in
 *                 binary format = number of coratings for each movie-movie
 *                 pair (num users who rated both movies).
 * d:    shrinkage factor (see notes below); defaults to 0
 * beta: shrinkage factor (see notes below); defaults to 0
 * 
 * terms has format:
 * term num_pages num_movies movie_id movie_id ...
 * term num_pages num_movies movie_id movie_id ...
 * ...
 * where the page for each movie_id links to the corresponding term.
 *
 * Output (on standard out):
 * term weight num_coratings
 * term weight num_coratings
 * ...
 *
 * To compute the average weighted similarity of movies, we use:
 * avg_similarity = sum_{m,n \in M(q), m != n} U(m,n)*S(m,n)}
 *                  -----------------------------------------
 *                  beta + sum_{m,n \in M(q), m != n} U(m,n)
 * where M(q) is the set of movies with pages mentioning the link in question,
 * beta is a shrinkage factor, and U is the number of coratings.
 *
 * Then we need to shrink with respect to the number of movies in M(q); because
 * of the possibility of several movies mapping to one page, it's better to
 * shirnk toward the number of pages corresponding to movies in M(q).
 *    (num_pages*avg_similarity) / (num_pages + d)
 */ 
#include <fstream>
#include <iostream>
#include <set>
#include "u1.h"
#include "symmetric_matrix.h"

using namespace std;

int main(int argc, char **argv)
{
  if (argc < 4 || argc > 6) {
    TRACE<<"see comments for usage"<<endl;
    exit(EXIT_FAILURE);
  }
  string num_movies_str = argv[1];
  string similarity_file = argv[2];
  string coratings_file = argv[3];
  float d = 0;
  if (argc > 4) d = atof(argv[4]);
  if (d < 0) {
    TRACE<<"invalid d"<<d<<endl;
    exit(EXIT_FAILURE);
  }
  float beta = 0;
  if (argc > 5) beta = atof(argv[5]);
  if (beta < 0) {
    TRACE<<"invalid beta"<<beta<<endl;
    exit(EXIT_FAILURE);
  }

  int num_sim_movies = atoi(num_movies_str.c_str());
  TRACE<<"Number of movies: "<<num_sim_movies<<endl;

  TRACE<<"Reading similarity from "<<similarity_file<<endl;
  symmetric_matrix<float> similarity(num_sim_movies);
  ifstream similarity_in(similarity_file.c_str());
  similarity.read_binary(similarity_in);
  similarity_in.close();

  TRACE<<"Reading coratings from "<<coratings_file<<endl;
  symmetric_matrix<float> coratings(num_sim_movies);
  ifstream coratings_in(coratings_file.c_str());
  coratings.read_binary(coratings_in);
  coratings_in.close();

  TRACE<<"Reading terms and movies from stdin."<<endl;
  int term, movie_id;
  int num_pages, num_movies;
  vector<int> movies;
  while (cin>>term>>num_pages>>num_movies) {
    movies.clear();
    movies.reserve(num_movies);
    for (int i = 0; i < num_movies; ++i) {
      if (!(cin>>movie_id)) {
        TRACE<<"Expected "<<num_movies<<" movies for term "<<term
          <<" but got "<<i<<endl;
        exit(EXIT_FAILURE);
      }
      movies.push_back(movie_id);
    }

    // Don't bother with terms appearing on only one movie.
    if (movies.size() < 2) continue;

    // Weighted average of similarities.
    float sum = 0;
    float U_sum = 0;
    for (size_t i = 0; i < movies.size(); ++i) {
      for (size_t j = i + 1; j < movies.size(); ++j) {
        int movie_i = movies[i];
        int movie_j = movies[j];
        float U = coratings(movie_i-1,movie_j-1);
        sum += U*similarity(movie_i-1,movie_j-1);
        U_sum += U;
      }
    }
    float average = 0;
    if (U_sum > 0) average = sum / (U_sum + beta);

    // Shrink with respect to number of pages.
    if (d > 0) {
      average = (num_pages*average) / (num_pages + d);
    }

    cout<<term<<' '<<average<<' '<<U_sum<<endl;
  }

  TRACE<<"Done."<<endl;
  return EXIT_SUCCESS;
}
/*
* Copyright (c) 2009 John Lees-Miller
* 
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
* 
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
* 
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/

